local snow     = require("meet_your_mods.mhr").snow
local System   = require("meet_your_mods.mhr").System
local status   = require("meet_your_mods.status")
local shortcut = require("meet_your_mods.shortcut")
local routine  = require("meet_your_mods.routine_framework")
local setting  = require("meet_your_mods.setting")
local ui       = require("meet_your_mods.ui")

--村区域
snow.stage.StageDef.AreaNoType:fetch_enum("training_area", "No05")
snow.stage.StageDef.AreaNoType:fetch_enum("cohoot_kamura", "No02")
snow.stage.StageDef.AreaNoType:fetch_enum("cohoot_elgado", "No06")
--海外交易村
snow.data.CommercialStuffId:fetch_enum("none", "CommercialStuff_None")
snow.data.CommercialStuffId:fetch_enum("kamura", "CommercialStuff_01")
snow.data.CommercialStuffId:fetch_enum("yukumo", "CommercialStuff_02")
snow.data.CommercialStuffId:fetch_enum("bherna", "CommercialStuff_03")
--道具
snow.data.ContentsIdSystem.ItemId:fetch_enum("business_acorn", "I_Normal_1041")
--交涉术
snow.facility.tradeCenter.NegotiationTypes:fetch_enum("none", "None")
snow.facility.tradeCenter.NegotiationTypes:fetch_enum("nobi", "Negotiation_000")
snow.facility.tradeCenter.NegotiationTypes:fetch_enum("seka", "Negotiation_001")
snow.facility.tradeCenter.NegotiationTypes:fetch_enum("kira", "Negotiation_002")
snow.facility.tradeCenter.NegotiationTypes:fetch_enum("super_nobi", "Negotiation_003")
snow.facility.tradeCenter.NegotiationTypes:fetch_enum("super_seka", "Negotiation_004")
snow.facility.tradeCenter.NegotiationTypes:fetch_enum("super_kira", "Negotiation_005")

local facility_bot = {
    setting      = {
        enable           = true, --显示村庄设施情况
        auto_loot_argosy = false, --自动拾取交易船货物
        auto_negotiate   = false, --自动设置交涉术
        auto_loot_stuff  = false, --自动拾取海外贸易货物
        auto_loot_cohoot = false, --自动掏鸟巢
        slient_mode      = true, --隐藏海外贸易和鸟巢的拾取结果
    },
    in_system    = false,
    negotiations = nil,
    status       = {},

    facility = nil,
    spy_unit = nil,
    dojo     = nil,
    cohoot   = nil,
    argosy   = nil,
    mystery  = nil,
    alchemy  = nil,
    stuff    = nil,

    icons      = {},
    font       = nil,
    pos_x      = 36,
    pos_y      = 1060,
    heigth     = 0,
    color_main = 0xCCFFFFFF,
    color_back = 0x55FFFFFF,
    speed      = 600,
    timer      = 0,
    fade_in    = false,
    opacity    = 1.0,

    message = {
        [status.languages.japanese]            = {
            kamura = "カムラの里",
            elgado = "エルガド",
            bherna = "ベルナ村",
            yukumo = "ユクモ村",
        },
        [status.languages.english]             = {
            kamura = "Kamura",
            elgado = "Elgado",
            bherna = "Bherna",
            yukumo = "Yukumo",
        },
        [status.languages.chinese_troditional] = {
            kamura = "神火村",
            elgado = "艾爾卡多",
            bherna = "貝魯納村",
            yukumo = "結雲村",
        },
        [status.languages.chinese_simplified]  = {
            kamura = "炎火村",
            elgado = "埃尔迦德",
            bherna = "贝鲁纳村",
            yukumo = "结云村",
        },
    },
}

--拾取交易船
function facility_bot:loot_argosy()
    for _, order in pairs(self.argosy:get_TradeOrderList()) do
        local treasure_list = order:get_IsUseMembershipInventoryFrame()
        for index, inventory in pairs(order:get_InventoryList()) do
            if inventory:isEmpty() then
                goto next_inventory
            end

            shortcut:add_item_to_box(inventory:get_ItemId(), inventory:get_Count())
            inventory:setEmpty()
            treasure_list[index] = false
            ::next_inventory::
        end
    end
end

--获取交涉术数据
local function fetch_negotiations(trade_center)
    local negotiations     = {}
    local negotiation_list = System.Collections.Generic.List(trade_center:get_NegotiationDataList())

    for _, negotiation in pairs(negotiation_list) do
        negotiation = negotiation._Param
        negotiations[negotiation._NegotiationType] = {
            type  = negotiation._NegotiationType,
            count = negotiation._Count,
            cost  = negotiation._Cost,
            level = negotiation._OtLv,
        }
    end

    negotiations[snow.facility.tradeCenter.NegotiationTypes.none] = {
        type  = snow.facility.tradeCenter.NegotiationTypes.none,
        count = 0,
        cost  = 0,
        level = 0,
    }

    return negotiations
end

--设置交涉术
function facility_bot:negotiate()
    if not self.negotiations then
        self.negotiations = fetch_negotiations(self.argosy)
    end

    local todo = {
        point     = 0,
        negotiate = false,
        use_acorn = true,
        type      = {
            [1] = snow.facility.tradeCenter.NegotiationTypes.none,
            [2] = snow.facility.tradeCenter.NegotiationTypes.none,
            [3] = snow.facility.tradeCenter.NegotiationTypes.none,
        }
    }
    for index, order in pairs(self.argosy:get_TradeOrderList()) do
        local otomo = order:get_OtomoData()
        if not otomo then
            todo.use_acorn = shortcut:get_item_quantity(snow.data.ContentsIdSystem.ItemId.business_acorn) ~= 0
            goto next_order
        end

        local type  = order:get_NegotiationType()
        local level = otomo:getLv()

        --当前没有设置交涉术时优先选择“慢悠悠”，其次选择“悠悠”
        if snow.facility.tradeCenter.NegotiationTypes.none == type then
            type = level >= self.negotiations[snow.facility.tradeCenter.NegotiationTypes.super_nobi].level
                and snow.facility.tradeCenter.NegotiationTypes.super_nobi
                or (level >= self.negotiations[snow.facility.tradeCenter.NegotiationTypes.nobi].level
                    and snow.facility.tradeCenter.NegotiationTypes.nobi
                    or snow.facility.tradeCenter.NegotiationTypes.none)
        end

        --检测随从等级、剩余点数、是否会溢出
        if snow.facility.tradeCenter.NegotiationTypes.none == type
            or level < self.negotiations[type].level
            or todo.point + self.negotiations[type].cost > shortcut:get_point()
            or order:get_NegotiationCount() + self.negotiations[type].count
            > order.NegotiationCountMax
        then
            todo.use_acorn = false
            goto next_order
        end
        todo.type[index] = type
        todo.point       = todo.point + self.negotiations[type].cost
        todo.negotiate   = true

        --检测使用橡果后是否会溢出
        if order:get_NegotiationCount() + self.negotiations[type].count
            + self.argosy:getAcornAddCount() > order.NegotiationCountMax
        then
            todo.use_acorn = false
        end
        ::next_order::
    end
    if not todo.negotiate
        or not todo.use_acorn
        and shortcut:get_item_quantity(snow.data.ContentsIdSystem.ItemId.business_acorn) ~= 0
    then
        return
    end

    --设置交涉术
    for index, order in pairs(self.argosy:get_TradeOrderList()) do
        if not order:get_OtomoData()
            or snow.facility.tradeCenter.NegotiationTypes.none == todo.type[index]
        then
            goto next_order
        end

        order._NegotiationType = todo.type[index]
        order:setNegotiationCount(order:get_NegotiationCount()
            + self.negotiations[todo.type[index]].count)
        order._CanChangeNegotiationType = false

        if todo.use_acorn then
            order._Boosted = true
        end
        ::next_order::
    end

    --消耗点数
    shortcut:consume_point(todo.point)
    --消耗橡果
    if todo.use_acorn then
        self.argosy:setAcorn(true)
        self.argosy:set_IsBoost(true)
        self.argosy._IsSetBoost = true
        shortcut:consume_item(snow.data.ContentsIdSystem.ItemId.business_acorn)
    end
end

--拾取鸟巢
function facility_bot:loot_cohoot()
    local save_data = self.cohoot:get_SaveData()
    if save_data._StackCount >= self.cohoot.StackItemCount then
        for _, item in pairs(save_data._StackItemID) do
            shortcut:add_item_to_box(item, 1, not self.setting.slient_mode)
            item = 0
        end
        save_data._StackCount = 0
    end

    if save_data._StackCount2 >= self.cohoot.StackItemCount then
        for _, item in pairs(save_data._StackItemID2) do
            shortcut:add_item_to_box(item, 1, not self.setting.slient_mode)
            item = 0
        end
        save_data._StackCount2 = 0
    end
end

--拾取海外交易货物
function facility_bot:loot_stuff()
    if not self.stuff:get_CanObtainlItem() then
        return
    end

    local item = self.stuff:lotCommercialItem()
    shortcut:add_item_to_box(item, 1, not self.setting.slient_mode)
    self.stuff:set_CanObtainlItem(false)
end

function facility_bot:auto_loot()
    if self.setting.auto_loot_argosy then
        self:loot_argosy()
    end

    if self.setting.auto_negotiate then
        self:negotiate()
    end

    if self.setting.auto_loot_cohoot then
        self:loot_cohoot()
    end

    if self.setting.auto_loot_stuff then
        self:loot_stuff()
    end
end

--获取交易船信息
function facility_bot:update_argosy()
    if not self.negotiations then
        self.negotiations = fetch_negotiations(self.argosy)
    end
    self.status.argosy = nil

    local argosy = {
        --有满载的交易船
        has_full = self.argosy:get_ExistsMaxReceiveInventory(),
        orders   = {},
    }

    --获取随从和交涉术信息
    for _, order in pairs(self.argosy:get_TradeOrderList()) do
        local otomo = order:get_OtomoData()
        if not otomo then
            goto next_order
        end

        --获取随从和交涉术信息
        local order = {
            otomo = {
                type  = otomo:getVariation(),
                level = otomo:getLv(),
            },
            negotiation = {
                type    = order:get_NegotiationType(),
                current = order:get_NegotiationCount()
                    + (self.argosy:isAcorn() and self.argosy:getAcornAddCount() or 0),
                total   = order.NegotiationCountMax,
            }
        }
        order.negotiation.can_negotiate = order.negotiation.current
            <= order.negotiation.total - self.negotiations[order.negotiation.type].count
        argosy.orders[#argosy.orders + 1] = order
        ::next_order::
    end

    if argosy.has_full or next(argosy.orders) then
        self.status.argosy = argosy
    end
end

--获取实验室信息
function facility_bot:update_mystery()
    self.status.mystery = nil

    local target = self.mystery:get_LaboTarget()
    local reward = self.mystery:get_LaboReward()
    if not target or not reward then
        return
    end

    self.status.mystery = {
        --任务进度
        current = reward:get_ClearCount(),
        --奖励任务数量
        total   = reward.FixRewardClearCount,
        --任务目标
        target  = shortcut:get_enemy_name(target:get_MainTargetEnemyType()),
        --目标等级
        level   = (target:get_QuestCondition()
            - snow.data.MysteryTargetQuestCondition.LvGratherThan1) * 10 + 1,
        --剩余回合数量
        left    = target:get_LimitQuestCycleCount() - target:get_QuestCycleCount(),
        --总回合数量
        max     = target:get_LimitQuestCycleCount(),
    }
end

--获取特价和抽奖信息
function facility_bot:update_sale()
    self.status.sale = nil
    if not self.facility:isSale() then
        return
    end

    local lottery_status = self.facility:checkItemShopLotEventStatus()
    self.status.sale = {
        --可以抽奖
        can_lottery = snow.facility.itemShop.ItemLotFunc.LotEventStatusTypes.MoneyAndTicket == lottery_status
            or snow.facility.itemShop.ItemLotFunc.LotEventStatusTypes.MoneyOnly == lottery_status
            or snow.facility.itemShop.ItemLotFunc.LotEventStatusTypes.TicketOnly == lottery_status, --可以抽奖
    }
end

--获取隐秘探险队信息
function facility_bot:update_spy_unit()
    self.status.spy_unit = nil
    if not self.spy_unit:get_IsOperating() then
        return
    end

    self.status.spy_unit = {
        --已探险回合数量
        current = self.spy_unit:get_NowStepCount(),
        --总回合数量
        total   = self.spy_unit.StepCountInRoute,
    }
end

--获取护石炼金信息
function facility_bot:update_alchemy()
    self.status.alchemy = nil

    local alchemy = {
        current = 0, --可收取罐子数量
        total   = 0, --总预订罐子数量
    }
    for _, order in pairs(self.alchemy:getReservedInfoList()) do
        local status = order:getReserveStatus()
        if snow.data.alchemy.ReserveInfo.ReserveStatusTypes.Wait == status then
            alchemy.total = alchemy.total + 1
        elseif snow.data.alchemy.ReserveInfo.ReserveStatusTypes.Outputted == status then
            alchemy.total   = alchemy.total + 1
            alchemy.current = alchemy.current + 1
        end
    end

    if alchemy.total ~= 0 then
        self.status.alchemy = alchemy
    end
end

--获取随从道场信息
function facility_bot:update_training()
    self.status.training = nil
    if not self.dojo:get_isDojoWorking()
        and not self.dojo:hasReport()
    then
        return
    end

    self.status.training = {
        --当前训练回合
        current = self.dojo:get_ChargeNum(),
        --充值回合
        total   = self.dojo:get_MaxChargeNum(),
        --训练结果
        otomo   = {},
    }
    for _, report in pairs(self.dojo:get_TrainingReportList()) do
        local otomo = report:get_OtomoData()
        if otomo then
            self.status.training.otomo[#self.status.training.otomo + 1] = {
                type  = otomo:getVariation(),
                level = report:get_AfterLv()
            }
        end
    end
end

--获取鸟巢信息
function facility_bot:update_cohoot()
    local save_data = self.cohoot:get_SaveData()
    self.status.cohoot = {
        --村随从广场鸟巢积累回合数
        kamura = save_data._StackCount,
        --调查据点鸟巢积累回合数
        elgado = save_data._StackCount2,
        --最大积累回合数
        total  = self.cohoot.StackItemCount,
    }
end

--获取水手平加尔的货物信息
function facility_bot:update_stuff()
    self.status.stuff = nil
    if not self.stuff:get_CanObtainlItem() then
        return
    end

    self.status.stuff = {
        --货物来源
        id      = self.stuff:get_CommercialStuffID(),
        --是否还有未取得过的货物
        focused = false,
    }

    --贸易的村庄是否还拥有尚未获取过的货物
    for _, item in pairs(self.stuff._Userdata._CommercialStuffList[self.status.stuff.id + 1]._LotItemList) do
        if 0 == shortcut:get_item_quantity(item._ItemId) then
            self.status.stuff.focused = true
            break
        end
    end
end

--更新NPC谈话信息
function facility_bot:update_talk()
    self.status.talk = snow.npc.NpcTalkMessageManager:get_singleton():get_DetermineSpeechBalloonAttribute()
end

--更新支线任务信息
function facility_bot:update_mission()
    self.status.mission = false
    for _, free_quest in pairs(snow.quest.MissionManager:get_singleton():get_FreeMissionWorkArray()) do
        if free_quest:get_isClear() then
            self.status.mission = true
            break
        end
    end
end

--更新设施信息
function facility_bot:update()
    if not self.setting.enable then
        return
    end

    self:update_argosy()
    self:update_mystery()
    self:update_sale()
    self:update_spy_unit()
    self:update_alchemy()
    self:update_training()
    self:update_cohoot()
    self:update_stuff()
    self:update_talk()
    self:update_mission()
end

function facility_bot:fetch_facility()
    self.facility = snow.data.FacilityDataManager:get_singleton()
    if self.facility then
        self.argosy  = self.facility._Trader._TradeFunc
        self.mystery = self.facility:getMysteryLaboFacility()
        self.alchemy = self.facility:getAlchemy()
        self.stuff   = self.facility:getCommercialStuffFacility()
    end

    self.spy_unit = snow.data.OtomoSpyUnitManager:get_singleton()
    self.dojo     = snow.facility.OtomoDojoFacility:get_singleton()
    self.cohoot   = snow.progress.ProgressOwlNestManager:get_singleton()
end

function facility_bot:pre_before_village(_)
    if not self.setting.enable then
        return sdk.PreHookResult.CALL_ORIGINAL
    end
    self:fetch_facility()

    --处理不需要屏显的拾取交易船，设置交涉术
    if self.setting.auto_loot_argosy then
        self:loot_argosy()
    end
    if self.setting.auto_negotiate then
        self:negotiate()
    end

    --更新设施信息
    self:update()

    return sdk.PreHookResult.CALL_ORIGINAL
end

function facility_bot:pre_return_village(_)
    self:fetch_facility()

    if self.setting.auto_loot_cohoot then
        self:loot_cohoot()
        self:update_cohoot()
    end

    if self.setting.auto_loot_stuff then
        self:loot_stuff()
        self:update_stuff()
    end

    return sdk.PreHookResult.CALL_ORIGINAL
end

function facility_bot:initialize()
    routine:hook(self)

    --更新NPC谈话信息
    snow.npc.NpcTalkMessageManager.updateDetermineSpeechBalloonAttribute:sdk_hook(nil, function(result)
        self:update_talk()
        return result
    end)

    --与NPC交谈后更新
    snow.npc.NpcTalkMessageManager.resetDetermineTalkAttribute:sdk_hook(nil, function(result)
        self:fetch_facility()
        self:update()
        return result
    end)

    --拾取鸟巢后更新
    snow.progress.ProgressOwlNestManager.supply:sdk_hook(nil, function(result)
        self:fetch_facility()
        self:update_cohoot()
        return result
    end)

    --关闭游戏内部怪异化研究的通知
    snow.gui.GuiManager.checkMysterySearchProgress:sdk_hook(function(_)
        return self.setting.enable and sdk.PreHookResult.SKIP_ORIGINAL or sdk.PreHookResult.CALL_ORIGINAL
    end)
    --关闭游戏内部炼金通知
    snow.gui.GuiManager.makaAlcemyAnaunce:sdk_hook(function(_)
        return self.setting.enable and sdk.PreHookResult.SKIP_ORIGINAL or sdk.PreHookResult.CALL_ORIGINAL
    end)

    snow.gui.GuiManager.updateCheckAnaunce:sdk_hook(
        function(_) self.in_system = true end,
        function(_) self.in_system = false end)

    --关闭游戏内部对ChatManager:reqAddChatInfomation的调用
    snow.gui.ChatManager.reqAddChatInfomation:sdk_hook(function(_)
        return (self.setting.enable and self.in_system) and sdk.PreHookResult.SKIP_ORIGINAL or
            sdk.PreHookResult.CALL_ORIGINAL
    end)

    --关闭游戏内部对ChatManager:reqAddChatClearQuestInfo
    snow.gui.ChatManager.reqAddChatClearQuestInfo:sdk_hook(function(parameters)
        return (self.setting.enable and self.in_system
            and snow.gui.ChatManager.MissionType.FreeMission == sdk.to_int64(parameters[3]))
            and sdk.PreHookResult.SKIP_ORIGINAL or sdk.PreHookResult.CALL_ORIGINAL
    end)

    setting:bind_setting("facility", self.setting)

    ui:cluster("gui", "UI增强")
    ui:checkbox("gui", "facility", "enable", "显示村庄设施情况")

    ui:cluster("autobot", "自动功能")
    ui:static_text("autobot", "设施增强")
    ui:checkbox("autobot", "facility", "auto_loot_argosy", "自动拾取交易船", nil, true)
    ui:checkbox("autobot", "facility", "auto_negotiate", "自动设置交涉术", nil, true)
    ui:checkbox("autobot", "facility", "auto_loot_stuff", "自动拾取海外贸易", nil, true)
    ui:checkbox("autobot", "facility", "auto_loot_cohoot", "自动掏鸟巢", nil, true)
    ui:checkbox("autobot", "facility", "slient_mode", "静默拾取海外贸易和鸟巢", nil, true)

    --在reframework重置后恢复
    local game_status = status:current_status()
    if status.logical_status.village == game_status
        or status.logical_status.training == game_status
    then
        self:fetch_facility()
        self:auto_loot()

        if status.logical_status.village == game_status then
            self:update()
        end
    end

    d2d.register(function() self:load_widgets() end, function() self:draw_widgets() end)
end

function facility_bot:normalize()
end

function facility_bot:load_widgets()
    self.font = d2d.Font.new("Noto Sans CJK SC", 14, true)

    self.icons.training = d2d.Image.new("meet_your_mods/training.png")
    self.icons.argosy   = d2d.Image.new("meet_your_mods/argosy.png")
    self.icons.cat      = d2d.Image.new("meet_your_mods/cat.png")
    self.icons.dog      = d2d.Image.new("meet_your_mods/dog.png")

    self.icons.cohoot = d2d.Image.new("meet_your_mods/cohoot.png")
    self.icons.kamura = d2d.Image.new("meet_your_mods/kamura.png")
    self.icons.elgado = d2d.Image.new("meet_your_mods/elgado.png")

    self.icons.alchemy  = d2d.Image.new("meet_your_mods/alchemy.png")
    self.icons.spy_unit = d2d.Image.new("meet_your_mods/spyunit.png")

    self.icons.mystery = d2d.Image.new("meet_your_mods/mystery.png")
    self.icons.quest   = d2d.Image.new("meet_your_mods/quest.png")

    self.icons.shop  = d2d.Image.new("meet_your_mods/shop.png")
    self.icons.stuff = d2d.Image.new("meet_your_mods/stuff.png")
    self.icons.talk  = d2d.Image.new("meet_your_mods/talk.png")
end

function facility_bot:get_color(color, blinking)
    return not blinking and color or (color & 0x00FFFFFF) + (math.floor((color >> 24) * self.opacity) << 24)
end

function facility_bot:get_opacity(opacity, color)
    return ((color or self.color_main) >> 24) / 0xFF * opacity
end

function facility_bot:draw_border(x, y, width, height, thickness, border)
    if not border or border[1] then
        d2d.fill_rect(x, y - height + thickness, thickness, height - thickness * 2, self.color_main)
    end
    if not border or border[2] then
        d2d.fill_rect(x, y - height, width, thickness, self.color_main)
    end
    if not border or border[3] then
        d2d.fill_rect(x + width - thickness, y - height + thickness, thickness, height - thickness * 2, self.color_main)
    end
    if not border or border[4] then
        d2d.fill_rect(x, y - thickness, width, thickness, self.color_main)
    end

    return width, height
end

function facility_bot:draw_progress(x, y, width, height, thickness, border, padding, current, total, blinking, simple,
                                    blinking_back)
    self:draw_border(x, y, width, height, thickness, border)

    local progress_width = (width - padding * 2
        - ((not border or border[1]) and thickness or 0)
        - ((not border or border[3]) and thickness or 0))
    local bar_width      = progress_width / total
    local bar_height     = height - thickness * 2 - padding * 2
    local start_x        = x + ((not border or border[1]) and thickness or 0) + padding
    local start_y        = y - thickness - padding - bar_height
    for index = 0, total - 1 do
        d2d.fill_rect(start_x + index * bar_width,
            start_y,
            index < total - 1 and bar_width - padding or progress_width - bar_width * index,
            bar_height,
            self:get_color(index < current and self.color_main or self.color_back,
                (index < current and blinking) or (index >= current and blinking_back)))
    end

    if not simple then
        local progress_text = string.format("%d/%d", current, total)
        local text_width, text_height = self.font:measure(progress_text)
        d2d.text(self.font, progress_text,
            x + width - text_width,
            y - height - text_height - padding,
            self.color_main)

        return width, height + text_height
    end

    return width, height
end

function facility_bot:draw_icon(x, y, size, thickness, border, icon, blinking)
    self:draw_border(x, y, size, size, thickness, border)
    d2d.image(icon, x + thickness, y - size + thickness,
        size - thickness * 2, size - thickness * 2, self:get_opacity(not blinking and 1.0 or self.opacity))

    return size, size
end

function facility_bot:draw_training()
    if not self.status.training then
        return
    end

    self:draw_icon(self.pos_x, self.pos_y, 48, 2, nil, self.icons.training,
        0 == self.status.training.current)
    self:draw_progress(self.pos_x + 48, self.pos_y, 640 - 48, 16, 2, { false, true, true, true }, 2,
        self.status.training.current, self.status.training.total,
        false, false, 0 == self.status.training.current)

    local offset = 0
    for _, otomo in pairs(self.status.training.otomo) do
        self:draw_icon(self.pos_x + 48 + 7 + offset, self.pos_y - 16 + 2, 32, 2, { true, true, true, false },
            snow.otomo.OtomoDef.OtVariation.OtAirou == otomo.type and self.icons.cat or self.icons.dog,
            otomo.level >= 50)

        local level = string.format("Lv.%2d", otomo.level)
        local text_width, text_height = self.font:measure(level)
        d2d.text(self.font,
            level,
            self.pos_x + 48 + 7 + offset + 32 + 2,
            self.pos_y - 16 - text_height,
            self:get_color(self.color_main, otomo.level >= 50))

        offset = offset + 32 + text_width + 25
    end

    self.pos_y = self.pos_y - 48 - 16
end

function facility_bot:draw_argosy()
    if not self.status.argosy then
        return
    end

    self:draw_icon(self.pos_x, self.pos_y, 48, 2, nil, self.icons.argosy, self.status.argosy.has_full)

    local length = (640 - 48 - 1 - #self.status.argosy.orders * 7) / #self.status.argosy.orders
    local pos_y  = self.pos_y - math.floor((48 - 30 - 16) / 2)
    for index, order in pairs(self.status.argosy.orders) do
        local pos_x = self.pos_x + 48 + 1 + index * 7 + (index - 1) * length
        self:draw_progress(pos_x, pos_y, length, 16, 2, nil, 2,
            order.negotiation.current, order.negotiation.total,
            order.negotiation.can_negotiate, false, 0 == order.negotiation.current)

        self:draw_icon(pos_x, pos_y - 16 + 2, 32, 2, { true, true, true, false },
            snow.otomo.OtomoDef.OtVariation.OtAirou == order.otomo.type and self.icons.cat or self.icons.dog,
            false)

        local level = string.format("Lv.%2d", order.otomo.level)
        local _, text_height = self.font:measure(level)
        d2d.text(self.font,
            level,
            pos_x + 32 + 2,
            pos_y - 16 - text_height,
            self.color_main)
    end

    self.pos_y = self.pos_y - (48 + 16)
end

function facility_bot:draw_cohoot()
    if not self.status.cohoot then
        return
    end

    local cohoot = {
        {
            icon    = self.icons.kamura,
            name    = self.message[status:display_language()].kamura,
            current = self.status.cohoot.kamura,
            total   = self.status.cohoot.total,
        },
        {
            icon    = self.icons.elgado,
            name    = self.message[status:display_language()].elgado,
            current = self.status.cohoot.elgado,
            total   = self.status.cohoot.total,
        },
    }

    self:draw_icon(self.pos_x, self.pos_y, 48, 2, nil, self.icons.cohoot)

    local length = (640 - 48 - 1 - 14) / 2
    local pos_y  = self.pos_y - math.floor((48 - 30 - 16) / 2)
    for index, cohoot in pairs(cohoot) do
        local pos_x = self.pos_x + 48 + 1 + index * 7 + (index - 1) * length
        self:draw_progress(pos_x, pos_y, length, 16, 2, nil, 2,
            cohoot.current, cohoot.total, cohoot.current >= cohoot.total)

        self:draw_icon(pos_x, pos_y - 16 + 2, 32, 2, { true, true, true, false },
            cohoot.icon, false)

        local _, text_height = self.font:measure(cohoot.name)
        d2d.text(self.font, cohoot.name, pos_x + 32 + 2, pos_y - 16 - text_height, self.color_main)
    end

    self.pos_y = self.pos_y - 48 - 16
end

function facility_bot:draw_mystery()
    if not self.status.mystery then
        return
    end

    self:draw_icon(self.pos_x, self.pos_y, 48, 2, nil, self.icons.mystery,
        self.status.mystery.current >= self.status.mystery.total)
    self:draw_progress(self.pos_x + 48, self.pos_y, 640 - 48, 16, 2, { false, true, true, true }, 2,
        self.status.mystery.current, self.status.mystery.total,
        self.status.mystery.current >= self.status.mystery.total)

    local target = string.format("Lv.%3d %s", self.status.mystery.level, self.status.mystery.target)
    local text_width, text_height = self.font:measure(target)
    d2d.text(self.font, target, self.pos_x + 48 + 2, self.pos_y - 16 - 2 - text_height, self.color_main)

    for index = 1, self.status.mystery.max do
        d2d.image(self.icons.quest,
            self.pos_x + 48 + 2 + text_width + 8 + (index - 1) * 24,
            self.pos_y - 16 - 2 - 24,
            24, 24, self:get_opacity(1.0, index <= self.status.mystery.left and self.color_main or self.color_back))
    end

    self.pos_y = self.pos_y - 48 - 16
end

function facility_bot:draw_alchemy()
    if not self.status.alchemy then
        return
    end

    self:draw_icon(self.pos_x, self.pos_y, 48, 2, nil, self.icons.alchemy,
        self.status.alchemy.current >= self.status.alchemy.total)
    self:draw_progress(self.pos_x + 48, self.pos_y, 640 - 48, 16, 2, { false, true, true, true }, 2,
        self.status.alchemy.current, self.status.alchemy.total,
        self.status.alchemy.current >= self.status.alchemy.total)

    self.pos_y = self.pos_y - 48 - 16
end

function facility_bot:draw_spy_unit()
    if not self.status.spy_unit then
        return
    end

    self:draw_icon(self.pos_x, self.pos_y, 48, 2, nil, self.icons.spy_unit,
        self.status.spy_unit.current >= self.status.spy_unit.total)
    self:draw_progress(self.pos_x + 48, self.pos_y, 320 - 48, 16, 2, { false, true, true, true }, 2,
        self.status.spy_unit.current, self.status.spy_unit.total,
        self.status.spy_unit.current >= self.status.spy_unit.total)

    self.pos_y = self.pos_y - 48 - 16
end

function facility_bot:draw_top()
    local pos_x = self.pos_x

    if self.status.sale then
        self:draw_icon(pos_x, self.pos_y, 48, 2, nil, self.icons.shop, self.status.sale.can_lottery)
        pos_x = pos_x + 48 + 7
    end

    if self.status.stuff then
        self:draw_icon(pos_x, self.pos_y, 48, 2, nil, self.icons.stuff, self.status.stuff.focused)
        pos_x = pos_x + 48 + 7
    end

    if self.status.talk then
        self:draw_icon(pos_x, self.pos_y, 48, 2, nil, self.icons.talk, true)
        pos_x = pos_x + 48 + 7
    end

    if self.status.mission then
        self:draw_icon(pos_x, self.pos_y, 48, 2, nil, self.icons.quest, true)
        pos_x = pos_x + 48 + 7
    end

    self.pos_y = self.pos_y + 48 + 16
end

function facility_bot:draw_widgets()
    if status:gui_opening() or not self.setting.enable then
        return
    end

    --为闪动效果计算时间
    self.timer = self.timer + status:get_delta_time()
    if self.timer >= self.speed then
        self.timer   = 0
        self.opacity = self.fade_in and 1.0 or 0.0
        self.fade_in = not self.fade_in
    else
        self.opacity = self.fade_in and self.timer / self.speed or 1.0 - self.timer / self.speed
    end

    self.pos_y = 1044
    self:draw_training()
    self:draw_argosy()
    self:draw_cohoot()
    self:draw_mystery()
    self:draw_alchemy()
    self:draw_spy_unit()
    self:draw_top()
end

return facility_bot
